package com.tensai.core.mvc.base.tools;

import com.tensai.core.constant.HqlMark;
import com.tensai.core.mvc.base.entry.BaseEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.Map.Entry;

/**
 * HQL过滤器，用于添加where条件和排序，过滤结果集
 * <p>
 * 添加规则使用addFilter方法
 * <p>
 * 举例：QUERY_id_S_EQ = 0 //最终连接出的HQL是 and t.id = :id id的值是0通过参数传递给Dao
 * <p>
 * 格式说明QUERY前缀就说明要添加过滤条件
 * <p>
 * t#id 就是t.id
 * <p>
 * S:String L:Long I:Integer D:Date ST:Short BD:BigDecimal FT:Float
 * <p>
 * EQ 是操作符
 * <p>
 * // EQ 相等 // NE 不等 // LT 小于 // GT 大于 // LE 小于等于 // GE 大于等于 // LK 模糊 // RLK 右模糊
 * // LLK 左模糊//SGE 字符串大于等于cd
 *
 * @author tensai
 */
public class HqlFilter<T extends BaseEntity> {
	
	/**
	 * 条件参数
	 */
	private Map<String, String> params = new HashMap<>();
	
	/**
	 * 默认构造
	 */
	public HqlFilter() {
	}
	
	/**
	 * 带参构造
	 *
	 * @param request 请求
	 */
	public HqlFilter(HttpServletRequest request) {
		addFilter(request);
	}
	
	/**
	 * 获得过滤字段参数和值
	 *
	 * @return 参数
	 */
	public Map<String, String> getParams() {
		return params;
	}
	
	/**
	 * 添加过滤
	 *
	 * @param request 请求
	 */
	private void addFilter(HttpServletRequest request) {
		Enumeration<String> names = request.getParameterNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String value = request.getParameter(name);
			if (!"".equals(value)) {
				params.put(name, value.trim());
			}
		}
	}
	
	/**
	 * 添加过滤
	 * 举例，name传递：id_S_EQ
	 * 举例，value传递：0
	 *
	 * @param name  参数名
	 * @param value 参数值
	 */
	public void addFilter(String name, String value) {
		if (name != null) {
			params.put("QUERY_" + name, value);
		}
	}

	/**
	 *
	 * @return
	 */
	public Specification<T> getSpecification() {
		return (root, query, cb) -> {
			List<Predicate> list = new ArrayList<>();
			for (Entry<String, String> entry : params.entrySet()) {
				if (!entry.getKey().startsWith("QUERY_")) {
					continue;
				}
				String[] filterParams = StringUtils.split(entry.getKey().replace("QUERY_", ""), "_");
				if (filterParams.length == 3) {
					// 字段名
					String columnName = filterParams[0];
					// 字段类型
					String columnType = filterParams[1];
					// SQL操作符
					String operator = filterParams[2];
					String[] columns = columnName.split("\\.");
					//相等
					if (StringUtils.equalsIgnoreCase(operator, "EQ")) {
						Path<Object> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.equal(p, getObjValue(columnType, operator, entry.getValue())));
					}
					//不等
					if (StringUtils.equalsIgnoreCase(operator, "NE")) {
						Path<Object> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.notEqual(p, getObjValue(columnType, operator, entry.getValue())));
					}
					if (StringUtils.equalsIgnoreCase(operator, "LT")) {
						Path<Number> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.lt(p, (Number) getObjValue(columnType, operator, entry.getValue())));
					}
					if (StringUtils.equalsIgnoreCase(operator, "GT")) {
						Path<Number> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.gt(p, (Number) getObjValue(columnType, operator, entry.getValue())));
					}
					if (StringUtils.equalsIgnoreCase(operator, "LE")) {
						Path<Number> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.le(p, (Expression<? extends Number>) getObjValue(columnType, operator, entry.getValue())));
					}
					if (StringUtils.equalsIgnoreCase(operator, "GE")) {
						Path<Number> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.ge(p, (Number) getObjValue(columnType, operator, entry.getValue())));
					}
					
					if (StringUtils.equalsIgnoreCase(operator, "SGE")) {
						Path<String> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.greaterThanOrEqualTo(p, (String) getObjValue(columnType, operator, entry.getValue())));
					}
					
					if (StringUtils.equalsIgnoreCase(operator, "SLE")) {
						Path<String> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.lessThanOrEqualTo(p, (String) getObjValue(columnType, operator, entry.getValue())));
					}
					
					if (StringUtils.equalsIgnoreCase(operator, "LK") || StringUtils.equalsIgnoreCase(operator, "RLK") || StringUtils.equalsIgnoreCase(operator, "LLK")) {
						Path<String> p = root.get(columns[0]);
						for (int i = 1; i < columns.length; i++) {
							p = p.get(columns[i]);
						}
						list.add(cb.like(p, (String) getObjValue(columnType, operator, entry.getValue())));
					}
				}
			}
			if (list.size() == 0) {
				return null;
			}
			Predicate[] predicates = new Predicate[list.size()];
			predicates = list.toArray(predicates);
			return cb.and(predicates);
		};
	}
	
	/**
	 * 将String值转换成Object，用于拼写HQL，替换操作符和值
	 * <p>
	 * S:String L:Long I:Integer D:Date ST:Short BD:BigDecimal FT:Float
	 *
	 * @param columnType    字段数据类型
	 * @param operator      操作类型
	 * @param value         字段值
	 * @return              转换数据
	 */
	private Object getObjValue(String columnType, String operator, String value) {
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_S)) {
			if (StringUtils.equalsIgnoreCase(operator, HqlMark.OPERATOR_LK)) {
				value = "%" + value + "%";
			} else if (StringUtils.equalsIgnoreCase(operator, HqlMark.OPERATOR_RLK)) {
				value = value + "%";
			} else if (StringUtils.equalsIgnoreCase(operator, HqlMark.OPERATOR_LLK)) {
				value = "%" + value;
			}
			return value;
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_L)) {
			return Long.parseLong(value);
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_I)) {
			return Integer.parseInt(value);
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_D) && value != null && !"".equals(value)) {
			try {
				return DateUtils.parseDate(value, new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd", "yyyy/MM/dd"});
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_ST)) {
			return Short.parseShort(value);
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_BD)) {
			return BigDecimal.valueOf(Long.parseLong(value));
		}
		if (StringUtils.equalsIgnoreCase(columnType, HqlMark.TYPE_FT)) {
			return Float.parseFloat(value);
		}
		return null;
	}
	
}
